package org.csu.softwaremetrics_demo.metric.tradition;

import org.apache.commons.lang3.StringUtils;
import org.csu.softwaremetrics_demo.entity.common.TraditionMetricSingleClass;
import org.csu.softwaremetrics_demo.entity.result.Result;
import org.csu.softwaremetrics_demo.metric.Metric;
import org.eclipse.jdt.core.dom.*;

import java.util.HashMap;
import java.util.Map;

/**
 * @Description: CC(cyclomatic complexity) 圈复杂度度量
 * @Author: Jukomu
 * @Package: org.csu.softwaremetrics_demo.metric.tradition
 * @Project: SoftwareMetrics_demo
 * @name: CC
 * @Date: 2025/4/8-19:09
 * @Filename: CC
 */
public class CC extends ASTVisitor implements Metric<TraditionMetricSingleClass> {

    /**
     * 存储当前类的圈复杂度总和
     * 每个控制流结构和条件都会增加这个值
     */
    private int cc; // 方法圈复杂度
    private int ccClass; // 类圈复杂度
    private Map<String, Integer> ccMethod; // 类所有方法的复杂度


    public CC() {
        this.cc = 0;
        this.ccClass = 0;
        this.ccMethod = new HashMap<>();
    }

    /**
     * 访问方法声明节点时调用
     * 增加圈复杂度并继续访问子节点
     * @param node 方法声明节点
     * @return 是否继续访问子节点
     */
    public boolean visit(MethodDeclaration node) {
        ccClass += cc; // 将当前方法的圈复杂度添加到类圈复杂度中
        cc = 0; // 重置ccClass变量
        increaseCc();
        return super.visit(node);
    }

    @Override
    public void endVisit(MethodDeclaration node) {
        ccMethod.put(node.getName().toString(), cc); // 将ccClass的值添加到ccMethod映射中
    }


    /**
     * 访问for循环语句节点时调用
     * 增加圈复杂度并继续访问子节点
     * @param node for循环语句节点
     * @return 是否继续访问子节点
     */
    @Override
    public boolean visit(ForStatement node) {
        increaseCc();
        return super.visit(node);
    }


    /**
     * 访问增强型for循环语句节点时调用
     * 增加圈复杂度并继续访问子节点
     * @param node 增强型for循环语句节点
     * @return 是否继续访问子节点
     */
    @Override
    public boolean visit(EnhancedForStatement node) {
        increaseCc();
        return super.visit(node);
    }


    /**
     * 访问条件表达式节点时调用
     * 增加圈复杂度并继续访问子节点
     * @param node 条件表达式节点
     * @return 是否继续访问子节点
     */
    @Override
    public boolean visit(ConditionalExpression node) {
        increaseCc();
        return super.visit(node);
    }


    /**
     * 访问do-while循环语句节点时调用
     * 增加圈复杂度并继续访问子节点
     * @param node do-while循环语句节点
     * @return 是否继续访问子节点
     */
    @Override
    public boolean visit(DoStatement node) {
        increaseCc();
        return super.visit(node);
    }


    /**
     * 访问while循环语句节点时调用
     * 增加圈复杂度并继续访问子节点
     * @param node while循环语句节点
     * @return 是否继续访问子节点
     */
    @Override
    public boolean visit(WhileStatement node) {

        increaseCc();
        return super.visit(node);
    }

    /**
     * 访问switch-case语句节点时调用
     * 如果不是默认case，则增加圈复杂度并继续访问子节点
     * @param node switch-case语句节点
     * @return 是否继续访问子节点
     */
    @Override
    public boolean visit(SwitchCase node) {
        if (!node.isDefault())
            increaseCc();
        return super.visit(node);
    }

    /**
     * 访问初始化块节点时调用
     * 增加圈复杂度并继续访问子节点
     * @param node 初始化块节点
     * @return 是否继续访问子节点
     */
    @Override
    public boolean visit(Initializer node) {
        increaseCc();
        return super.visit(node);
    }

    /**
     * 访问catch语句节点时调用
     * 增加圈复杂度并继续访问子节点
     * @param node catch语句节点
     * @return 是否继续访问子节点
     */
    @Override
    public boolean visit(CatchClause node) {
        increaseCc();
        return super.visit(node);
    }

    /**
     * 访问if语句节点时调用
     * 计算逻辑运算符的数量并增加相应的圈复杂度
     * 然后继续访问子节点
     *
     * @param node if语句节点
     * @return 是否继续访问子节点
     */
    public boolean visit(IfStatement node) {
        String expr = node.getExpression().toString().replace("&&", "&").replace("||", "|");
        int ands = StringUtils.countMatches(expr, "&");
        int ors = StringUtils.countMatches(expr, "|");

        increaseCc(ands + ors);
        increaseCc();

        return super.visit(node);
    }

    /**
     * 将圈复杂度增加1
     * 用于处理基本控制流结构
     */
    private void increaseCc() {
        increaseCc(1);
    }

    /**
     * 增加指定数量的圈复杂度
     * 用于处理复杂条件表达式（包含多个逻辑运算符）
     *
     * @param qtd 要增加的复杂度数量
     */
    protected void increaseCc(int qtd) {
        cc++;
    }

    @Override
    public void execute(CompilationUnit cu, TraditionMetricSingleClass singleMetric, Result result) {
        cu.accept(this);
    }
    @Override
    public void setSingleMetric(TraditionMetricSingleClass singleMetric) {
        singleMetric.setCc(ccClass);
        singleMetric.setCcMethod(ccMethod);
    }
}
